package org.farng.mp3.id3;

import org.farng.mp3.InvalidTagException;
import org.farng.mp3.TagConstant;
import org.farng.mp3.TagUtility;

import java.io.IOException;
import java.io.RandomAccessFile;


/**
 * Title:       ID3v2_3Frame Description: This class is the tag frame header
 * used for ID3v2.30 tags Copyright:   Copyright (c) 2002 Company:
 *
 * @author Eric Farng
 * @version $Revision: 1.8 $
 */
public class ID3v2_3Frame extends ID3v2_2Frame {
    /** DOCUMENT ME! */
    protected boolean compression = false;

    /** DOCUMENT ME! */
    protected boolean encryption = false;

    /** DOCUMENT ME! */
    protected boolean fileAlterPreservation = false;

    /** DOCUMENT ME! */
    protected boolean groupingIdentity = false;

    /** DOCUMENT ME! */
    protected boolean readOnly = false; // @todo implement this read only!

    // these are flags for each frame them selves

    /** DOCUMENT ME! */
    protected boolean tagAlterPreservation = false;

    /**
     * Creates a new ID3v2_3Frame object.
     */
    public ID3v2_3Frame() {
        setAlterPreservation();
    }

    public ID3v2_3Frame(ID3v2_3Frame copyObject) {
        super(copyObject);
        this.compression           = copyObject.compression;
        this.encryption            = copyObject.encryption;
        this.fileAlterPreservation = copyObject.fileAlterPreservation;
        this.groupingIdentity      = copyObject.groupingIdentity;
        this.readOnly              = copyObject.readOnly;
        this.tagAlterPreservation  = copyObject.tagAlterPreservation;
    }

    /**
     * Creates a new ID3v2_3Frame object.
     *
     * @param readOnly DOCUMENT ME!
     * @param groupingIdentity DOCUMENT ME!
     * @param compression DOCUMENT ME!
     * @param encryption DOCUMENT ME!
     * @param body DOCUMENT ME!
     */
    public ID3v2_3Frame(boolean readOnly, boolean groupingIdentity, boolean compression, boolean encryption, AbstractID3v2FrameBody body) {
        this.body             = body;
        this.readOnly         = readOnly;
        this.groupingIdentity = groupingIdentity;
        this.compression      = compression;
        this.encryption       = encryption;
        setAlterPreservation();
    }

    /**
     * Creates a new ID3v2_4Frame object.
     *
     * @param frame DOCUMENT ME!
     */
    public ID3v2_3Frame(AbstractID3v2Frame frame) {
        if (frame instanceof ID3v2_3Frame) {
            ID3v2_3Frame f = (ID3v2_3Frame) frame;
            this.tagAlterPreservation  = f.tagAlterPreservation;
            this.fileAlterPreservation = f.fileAlterPreservation;
            this.readOnly              = f.readOnly;
            this.groupingIdentity      = f.groupingIdentity;
            this.compression           = f.compression;
            this.encryption            = f.encryption;
        }

        if (frame instanceof ID3v2_2Frame) {
            // no variables yet
        }

        if (frame.getBody() == null) {
            // do nothing
        } else if (TagUtility.isID3v2_3FrameIdentifier(frame.getIdentifier())) {
            this.body = (AbstractID3v2FrameBody) TagUtility.copyObject(frame.getBody());
        }
    }

    /**
     * Creates a new ID3v2_3Frame object.
     *
     * @param file DOCUMENT ME!
     *
     * @throws IOException DOCUMENT ME!
     * @throws InvalidTagException DOCUMENT ME!
     */
    public ID3v2_3Frame(RandomAccessFile file)
                 throws IOException, InvalidTagException {
        this.read(file);
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public int getSize() {
        return body.getSize() + 4 + 2 + 4;
    }

    /**
     * DOCUMENT ME!
     *
     * @param obj DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public boolean equals(Object obj) {
        if ((obj instanceof ID3v2_3Frame) == false) {
            return false;
        }

        ID3v2_3Frame object = (ID3v2_3Frame) obj;

        if (this.compression != object.compression) {
            return false;
        }

        if (this.encryption != object.encryption) {
            return false;
        }

        if (this.fileAlterPreservation != object.fileAlterPreservation) {
            return false;
        }

        if (this.groupingIdentity != object.groupingIdentity) {
            return false;
        }

        if (this.readOnly != object.readOnly) {
            return false;
        }

        if (this.tagAlterPreservation != object.tagAlterPreservation) {
            return false;
        }

        return super.equals(obj);
    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @throws IOException DOCUMENT ME!
     * @throws InvalidTagException DOCUMENT ME!
     */
    public void read(RandomAccessFile file)
              throws IOException, InvalidTagException {
        byte   b;
        long   filePointer;
        byte[] buffer = new byte[4];

        // lets scan for a non-zero byte;
        do {
            filePointer = file.getFilePointer();
            b           = file.readByte();
            org.farng.mp3.id3.AbstractID3v2.paddingCounter++;
        } while (b == 0);

        file.seek(filePointer);
        org.farng.mp3.id3.AbstractID3v2.paddingCounter--;

        // read the four character identifier
        file.read(buffer, 0, 4);

        String identifier = new String(buffer, 0, 4);

        // is this a valid identifier?
        if (isValidID3v2FrameIdentifier(identifier) == false) {
            file.seek(file.getFilePointer() - 3);
            throw new InvalidTagException(identifier + " is not a valid ID3v2.30 frame");
        }

        filePointer = file.getFilePointer();

        // skip the 4 byte size
        file.skipBytes(4);

        // read the flag bytes
        file.read(buffer, 0, 2);

        tagAlterPreservation  = (buffer[0] & TagConstant.MASK_V23_TAG_ALTER_PRESERVATION) != 0;
        fileAlterPreservation = (buffer[0] & TagConstant.MASK_V23_FILE_ALTER_PRESERVATION) != 0;
        readOnly              = (buffer[0] & TagConstant.MASK_V23_READ_ONLY) != 0;

        compression      = (buffer[1] & TagConstant.MASK_V23_COMPRESSION) != 0;
        encryption       = (buffer[1] & TagConstant.MASK_V23_ENCRYPTION) != 0;
        groupingIdentity = (buffer[1] & TagConstant.MASK_V23_GROUPING_IDENTITY) != 0;

        file.seek(filePointer);
        body = readBody(identifier, file);
    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @throws IOException DOCUMENT ME!
     */
    public void write(RandomAccessFile file)
               throws IOException {
        long   filePointer;
        byte[] buffer = new byte[4];

        String str = TagUtility.truncate(getIdentifier(), 4);

        for (int i = 0; i < str.length(); i++) {
            buffer[i] = (byte) str.charAt(i);
        }

        file.write(buffer, 0, str.length());

        filePointer = file.getFilePointer();

        // skip the size bytes
        file.skipBytes(4);

        setAlterPreservation();

        buffer[0] = 0;
        buffer[1] = 0;

        if (tagAlterPreservation) {
            buffer[0] |= TagConstant.MASK_V23_TAG_ALTER_PRESERVATION;
        }

        if (fileAlterPreservation) {
            buffer[0] |= TagConstant.MASK_V23_FILE_ALTER_PRESERVATION;
        }

        if (readOnly) {
            buffer[0] |= TagConstant.MASK_V23_READ_ONLY;
        }

        if (compression) {
            buffer[1] |= TagConstant.MASK_V23_COMPRESSION;
        }

        if (encryption) {
            buffer[1] |= TagConstant.MASK_V23_ENCRYPTION;
        }

        if (groupingIdentity) {
            buffer[1] |= TagConstant.MASK_V23_GROUPING_IDENTITY;
        }

        file.write(buffer, 0, 2);

        file.seek(filePointer);
        body.write(file);
    }

    /**
     * DOCUMENT ME!
     */
    protected void setAlterPreservation() {
        String str = getIdentifier();

        if (str.equals("ETCO") || str.equals("EQUA") || str.equals("MLLT") || str.equals("POSS") || str.equals("SYLT") || str.equals("SYTC") || str.equals("RVAD") || str.equals("TENC") || str.equals("TLEN") || str.equals("TSIZ")) {
            tagAlterPreservation  = false;
            fileAlterPreservation = true;
        } else {
            tagAlterPreservation  = false;
            fileAlterPreservation = true;
        }
    }
}